package com.wt.springsamples.security.config;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import com.wt.springsamples.security.exception.GlobalAccessDeniedHandler;
import com.wt.springsamples.security.exception.GlobalAuthenticationEntryPoint;
import com.wt.springsamples.security.handler.ApiLogoutSuccessHandler;
import com.wt.springsamples.security.handler.FormLoginAuthenticationFailureHandler;
import com.wt.springsamples.security.handler.GenerateTokenAuthenticationSuccessHandler;
import com.wt.springsamples.security.jwt.JwtAccessDeniedHandler;
import com.wt.springsamples.security.jwt.JwtAuthenticationEntryPoint;
import com.wt.springsamples.security.jwt.RedisBearerTokenResolver;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import static org.springframework.security.config.Customizer.withDefaults;


@Configuration
@EnableWebSecurity //没有springboot对应的starter时需要配置，会额外配置OAuth2.0等非必须配置
@EnableGlobalMethodSecurity(prePostEnabled = true) //需要使用 @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter等注解时需要配置
public class SecurityConfig extends GlobalMethodSecurityConfig {

//    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
//        return (web) -> web.ignoring().antMatchers("/resources/**");
        return (web) ->web.ignoring()
                .antMatchers(HttpMethod.GET,
                        "/swagger-resources/**",
                        "/images/**",
                        "/assets/**",
                        "/**/*.png",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/favicon.ico",
                        "/webjars/**",
                        "/v2/**",
                        "/druid/**");
    }
    /**
     * 当添加cookie时指定了不同源的domain时需要配置该Bean，但是会被浏览器安全策略阻止从而不会自动保存
     * @return
     */
//    @Bean
//    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
//        LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
//        legacyCookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
//        return (factory) -> factory.addContextCustomizers(
//
//                (context) -> context.setCookieProcessor(
//                        legacyCookieProcessor));
//    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
//                new OAuth2AuthorizationServerConfigurer();
//        RequestMatcher endpointsMatcher = authorizationServerConfigurer
//                .getEndpointsMatcher();
//        http.requestMatcher(endpointsMatcher);
        http
                .cors(withDefaults())//需要配置bean对象CorsConfigurationSource,参考官方说明
                .csrf().disable()
                .authorizeRequests((authorizeRequests)->authorizeRequests
                       // .antMatchers(HttpMethod.GET,"/api/login","/api/captcha","/api/order/**","/api/test/**").permitAll()
                         .antMatchers("/api/login","/api/captcha","/api/order/**","/api/test/**").permitAll()
                        .antMatchers("/api/**").authenticated()
//                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer().jwt(jwtConfigurer -> jwtConfigurer
                        .jwtAuthenticationConverter(buildJwtAuthenticationConverter()))
//                .bearerTokenResolver(new DefaultBearerTokenResolver())
                .bearerTokenResolver(new RedisBearerTokenResolver())
                .accessDeniedHandler(new JwtAccessDeniedHandler())
                .authenticationEntryPoint(new JwtAuthenticationEntryPoint())
                .and()
//                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .formLogin((formLogin)->formLogin
                                .successHandler(new GenerateTokenAuthenticationSuccessHandler())
//                              .successForwardUrl("/userinfo")//通过ForwardAuthenticationSuccessHandler实现
                                .failureHandler(new FormLoginAuthenticationFailureHandler())
                                .loginProcessingUrl("/api/login")
                )
                .logout((logout)->logout
                        .logoutUrl("/api/logout")
                        .logoutSuccessHandler(new ApiLogoutSuccessHandler()))
                //指定ExceptionHandlingConfigurer后DefaultLoginPageConfigurer不会注册LoginPageGeneratingFilter,参见DefaultLoginPageConfigurer的configure方法
                .exceptionHandling((exceptions) -> exceptions
//                        .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
//                        .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
                        .authenticationEntryPoint(new GlobalAuthenticationEntryPoint())
                        .accessDeniedHandler(new GlobalAccessDeniedHandler())
                );
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
//        config.addAllowedOrigin("*");
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return  source;
    }

    /**
     * JwtAuthenticationProvider从jwt中解析SCOP的值当成Authentication对象的权限列表 List<GrantedAuthority>
     * 并默认为权限列表的每一项添加SCOPE_前缀
     * @return
     */
    private  JwtAuthenticationConverter buildJwtAuthenticationConverter(){
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(""); //JWT权限默认会添加“SCOPE_"前缀
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

//    @Bean
//    public PasswordEncoder passwordEncoder() {
//        return new BCryptPasswordEncoder(12);
//    }

    @Value("${jwt.public.key}")
    RSAPublicKey key;

    @Value("${jwt.private.key}")
    RSAPrivateKey priv;

    @Bean
    public  PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(this.key).build();
    }

    @Bean
    JwtEncoder jwtEncoder() {
        JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }


}
